package com.wlf.server.generator;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.TemplateType;
import com.baomidou.mybatisplus.generator.config.builder.Controller;
import com.baomidou.mybatisplus.generator.config.builder.Entity;
import com.baomidou.mybatisplus.generator.config.querys.MySqlQuery;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wlf.server.ServerApplication;
import com.wlf.server.common.entity.SysGeneratorConfig;
import com.wlf.server.common.mapper.SysGeneratorConfigMapper;
import com.wlf.server.web.util.AjaxBean;
import com.wlf.server.web.util.ServletUtil;
import com.zaxxer.hikari.HikariDataSource;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
import org.springframework.http.ContentDisposition;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.FileCopyUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 代码生成
 */
@Slf4j
@Service
public class GeneratorService {
    @Resource
    private JdbcTemplate jdbcTemplate;
    @Resource
    private HikariDataSource hikariDataSource;

    @Resource
    private SysGeneratorConfigMapper generatorConfigMapper;
    @Resource
    private ObjectMapper objectMapper;

    private final static String vueTemp = "VUE";

    /**
     * 列表数据(暂时只兼容了Mysql8+)
     */
    public AjaxBean list(String name) {
        MySqlQuery sqlQuery = new MySqlQuery();
        String s = sqlQuery.tablesSql();
        if (StrUtil.isNotBlank(name)) {
            s += " and (Name like '%" + name + "%' or Comment like '%" + name + "%')";
        }
        List<Map<String, Object>> maps = jdbcTemplate.queryForList(s);
        return AjaxBean.getOkData(maps);
    }

    /**
     * 表的字段信息
     */
    public AjaxBean showTable(String name) {
        return AjaxBean.getOkData(jdbcTemplate.queryForList(
                StrUtil.format("show full columns from `{}`", name)
        ));
    }

    /**
     * 代码生成主方法
     */
    @SneakyThrows
    public void generator(GeneratorController.GlobalConfig globalConfig, List<String> tables) {
        // 保存配置
        SysGeneratorConfig config = new SysGeneratorConfig();
        config.setUserId(StpUtil.getLoginIdAsString());
        config.setGeneratorConfig(objectMapper.writeValueAsString(globalConfig));
        Long count = generatorConfigMapper.selectCount(new QueryWrapper<SysGeneratorConfig>().lambda()
                .eq(SysGeneratorConfig::getUserId, StpUtil.getLoginIdAsString()));
        if (count > 0) {
            generatorConfigMapper.updateById(config);
        } else {
            generatorConfigMapper.insert(config);
        }

        String tmpDirPath = FileUtil.getTmpDirPath() + IdWorker.get32UUID() + File.separator;
        String zipTmpDir = FileUtil.getTmpDirPath() + IdWorker.get32UUID() + ".zip";

        AtomicReference<Boolean> serviceInterfaceFlag = new AtomicReference<>(true);

        FastAutoGenerator.create(
                        hikariDataSource.getJdbcUrl(),
                        hikariDataSource.getUsername(),
                        hikariDataSource.getPassword())
                .globalConfig(builder -> {

                    builder.author(globalConfig.getAuthorName()); // 设置作者
                    builder.disableOpenDir();// 生成过后不打开文件夹

                    if (globalConfig.getEnableSwagger()) {
                        builder.enableSwagger();
                    }

                    builder.outputDir(tmpDirPath); // 指定输出目录

                })
                .packageConfig(builder -> builder.parent("") // 设置父包名
                        .moduleName("") // 设置父包模块名
                        .entity(globalConfig.getEntityName())
                        .service(globalConfig.getServiceName())
                        .serviceImpl(globalConfig.getServiceImplName())
                        .mapper(globalConfig.getMapperName())
                        .xml(globalConfig.getXmlName())
                        .controller(globalConfig.getControllerName()))
                .templateConfig(consumer -> {
                    List<String> templateTypes = new ArrayList<>(Arrays.stream(TemplateType.values()).map(TemplateType::name).toList());

                    List<String> tempList = globalConfig.getTempList();

                    templateTypes.removeAll(tempList);

                    consumer.xml("templates/mapper.xml");
                    consumer.mapper("templates/mapper.java");
                    consumer.service("templates/service.java");
                    consumer.serviceImpl("templates/serviceImpl.java");
                    consumer.controller("templates/controller.java");
                    for (String type : templateTypes) {
                        if (type.equals(TemplateType.SERVICE.name())) {
                            serviceInterfaceFlag.set(false);
                        }
                        consumer.disable(TemplateType.valueOf(type));
                    }
                    consumer.build();
                })
                .injectionConfig(consumer -> {
                    consumer.beforeOutputFile((tableInfo, stringObjectMap) -> {

                    });
                    consumer.customMap(Collections.singletonMap("serviceInterfaceFlag", serviceInterfaceFlag.get()));
                    if (globalConfig.getTempList().stream().anyMatch(i -> i.equals(vueTemp))) {
                        consumer.customFile(customFile -> customFile
                                .fileName("view.vue")
                                .templatePath("templates/view.vue.ftlh")
                                .packageName("view")
                                .build());
                    }
                    consumer.build();
                })
                .strategyConfig(builder -> {
                    builder.addInclude(tables);// 设置需要生成的表名
                    builder.addTablePrefix(globalConfig.getAddTablePrefix());
                    if (globalConfig.getEnableCapitalMode()) {
                        builder.enableCapitalMode();
                    }
                    Entity.Builder entityBuilder = builder.entityBuilder();
                    if (globalConfig.getEnableLombok()) {
                        entityBuilder.enableLombok();
                    }
                    entityBuilder.idType(globalConfig.getIdType());
                    entityBuilder.enableTableFieldAnnotation();
                    entityBuilder.build();

                    com.baomidou.mybatisplus.generator.config.builder.Mapper.Builder
                            mapperBuilder = builder.mapperBuilder();

                    mapperBuilder.mapperAnnotation(Mapper.class);
                    mapperBuilder.enableBaseResultMap();
                    mapperBuilder.enableBaseColumnList();
                    mapperBuilder.build();

                    com.baomidou.mybatisplus.generator.config.builder.Service.Builder serviceBuilder
                            = builder.serviceBuilder();
                    // 设置ServiceImpl文件名称
                    serviceBuilder.formatServiceImplFileName("%s" +
                            globalConfig.getServiceImplFilename());
                    // 设置Service文件名称
                    serviceBuilder.formatServiceFileName("%s"
                            + globalConfig.getServiceFileName());

                    serviceBuilder.build();

                    Controller.Builder controllerBuilder = builder.controllerBuilder();
                    controllerBuilder.enableRestStyle();
                    controllerBuilder.build();

                })
                .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板，默认的是Velocity引擎模板
                .execute();
        ZipUtil.zip(tmpDirPath, zipTmpDir);
        HttpServletResponse response = ServletUtil.getResponse();
        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION,
                ContentDisposition.attachment().filename(
                        DateUtil.date().toString(DatePattern.UTC_SIMPLE_PATTERN)
                                + tables.toString()
                                + ".zip"
                        , StandardCharsets.UTF_8).build().toString()
        );
        FileCopyUtils.copy(new FileInputStream(zipTmpDir), response.getOutputStream());
        FileUtil.del(zipTmpDir);
        FileUtil.del(tmpDirPath);
    }


    public AjaxBean initForm() {
        Dict dict = Dict.create();
        dict.set("packNames", getPackNames());
        SysGeneratorConfig sysGeneratorConfig = generatorConfigMapper.selectById(StpUtil.getLoginIdAsString());
        dict.set("generatorConfig", sysGeneratorConfig == null
                ? null
                : sysGeneratorConfig.getGeneratorConfig().replace("\\", "")
        );
        return AjaxBean.getOkData(dict);
    }
    /**
     * 获取项目包名
     *
     * @return 项目包名
     */
    @SneakyThrows
    private List<String> getPackNames() {
        HashSet<String> packNames = new HashSet<>();
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        String mainName = ServerApplication.class.getName();
        mainName = mainName.substring(0, mainName.lastIndexOf('.')).replace(".","/");
        org.springframework.core.io.Resource[] resources = resolver.getResources(StrUtil.format("classpath*:{}/**/*.class",
                mainName));
        for (org.springframework.core.io.Resource res : resources) {
            // 先获取resource的元信息，然后获取class元信息，最后得到 class 全路径
            String clsName = new SimpleMetadataReaderFactory().getMetadataReader(res).getClassMetadata().getClassName();
            String className = Class.forName(clsName).getName();
            packNames.add(className.substring(0, className.lastIndexOf('.')));
        }
        HashSet<String> hashSet = new HashSet<>();
        for (String packName : packNames) {
            StringBuilder temp = new StringBuilder();
            String[] split = packName.split("\\.");
            for (String s : split) {
                temp.append(s).append(".");
                hashSet.add(temp.substring(0, temp.length() - 1));
            }
        }
        return hashSet.stream().sorted(Comparator.comparing(String::length)).toList();
    }
}
